// TrajOutlierDetection.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include "MLTrajectoryLoader.h"
#include "MLTrajectorySaver.h"
#include "MLTRAODGrid.h"

void PrintTRAODGridUsage()
{
	std::cout<<"TrajectoryMining TRAODGrid [Trajectory Filename] [Result Filename]";
	std::cout<<"[outlyingDist] [outlyingFrac] [oytlyingPortion] ";
	std::cout<<"[GridLocSize] [GridDirSize] [SampleSize] ";
	std::cout<<"[LocWeight] [DirWeight] ";
	std::cout<<"\n";
}

void PrintTrajsSplitUsage()
{
	std::cout<<"TrajectoryMining trajsplit [Trajectory Filename] [Result Filename]";
	std::cout<<"[StartIndex] [EndIndex]";
	std::cout<<"\n";
}

void PrintReduceDuplicateUsage()
{
	std::cout<<"TrajectoryMining trajsplit [Trajectory Filename] [Result Filename]";
	std::cout<<"[StartIndex] [EndIndex]";
	std::cout<<"\n";
}

int TrajsReduceDuplicate(int argc, wchar_t* argv[])
{
	int nRet;
	CMLTrajectoryCollection trajs;
	CMLTrajectoryLoader loader;
	CMLTrajectorySaver saver;
	if (argc < 4)
	{
		PrintReduceDuplicateUsage();
		return -1;
	}
	std::wstring wstrSrcTrajFilename = argv[2];
	std::wstring wstrDestTrajFilename = argv[3];
	// Load the source trajectories
	if (loader.Initialize(wstrSrcTrajFilename) != 0)
	{
		std::wcout<<L"Cannot open the source trajectory file : "<<wstrSrcTrajFilename << L"!\n";
		return -1;
	}
	nRet = loader.Load(trajs);
	if (nRet != 0)
	{
		std::wcout<<L"Cannot load the source trajectory file : "<<wstrSrcTrajFilename << L"!\n";
		return -1;
	}
	loader.Close();
	// Insert the map
	std::multimap<std::wstring, CMLTrajectory*> mapTrajs;
	std::map<std::wstring, int> mapTrajNamesNo;
	wchar_t wszNewName[128];
	int i;
	for (i=0; i<trajs.Count(); i++)
	{
		CMLTrajectory& traj = trajs.Get(i);
		std::multimap<std::wstring, CMLTrajectory*>::iterator Iter;
		Iter = mapTrajs.find(traj.GetName());
		if (Iter != mapTrajs.end()) // Find a trajectory of the same name inserted before
		{
			CMLTrajectory* pSameTraj = (*Iter).second;
			if (pSameTraj->Equal(traj) == false)
			{
				std::wstring wszOriginalTrajName = traj.GetName();
				int nNameNo = mapTrajNamesNo[wszOriginalTrajName];
				std::wcout << wszOriginalTrajName << L"\n";
				swprintf(wszNewName, 128, L"%s%d", wszOriginalTrajName.c_str(), nNameNo);
				traj.SetName(wszNewName);
				// Insert the trajectory
				mapTrajs.insert(std::pair<std::wstring, CMLTrajectory*>(wszOriginalTrajName, &traj));
				// Increase the name number
				mapTrajNamesNo[wszOriginalTrajName] = nNameNo+1;
			}
		}
		else
		{
			// Insert the map
			mapTrajs.insert(std::pair<std::wstring, CMLTrajectory*>(traj.GetName(), &traj));
			mapTrajNamesNo[traj.GetName()] = 0;
		}
	}
	// Output the result trajectories from the map
	std::multimap<std::wstring, CMLTrajectory*>::iterator Iter;
	CMLTrajectoryCollection resultTrajs;
	for (Iter=mapTrajs.begin(); Iter!=mapTrajs.end(); Iter++)
	{
		CMLTrajectory* pTraj = (*Iter).second;
		resultTrajs.Add(*pTraj);
	}
	if (saver.Initialize(wstrDestTrajFilename) != 0)
	{
		std::wcout<<L"Cannot save the portion of trajectories to file : "<<wstrDestTrajFilename << L"!\n";
		return -1;
	}
	nRet = saver.Save(resultTrajs);
	if (nRet != 0)
	{
		std::wcout<<L"Cannot save the dest trajectories to file : "<<wstrDestTrajFilename << L"!\n";
		return -1;
	}
	saver.Close();

	// print result
	std::cout<< "Total "<<resultTrajs.Count() << " trajectories " << resultTrajs.GetTotalPointCount() << " points\n";
	return 0;
}

int TrajsSplit(int argc, wchar_t* argv[])
{
	int nRet;
	CMLTrajectoryCollection trajs;
	CMLTrajectoryLoader loader;
	CMLTrajectorySaver saver;
	if (argc< 6)
	{
		PrintTrajsSplitUsage();
		return -1;
	}
	// Read parameters 
	std::wstring wstrSrcTrajFilename = argv[2];
	std::wstring wstrDestTrajFilename = argv[3];
	std::wstring wstrStartIndex = argv[4];
	std::wstring wstrEndIndex = argv[5];
	int nStartIndex = _wtoi(wstrStartIndex.c_str());
	int nEndIndex = _wtoi(wstrEndIndex.c_str());
	// Load the source trajectories
	if (loader.Initialize(wstrSrcTrajFilename) != 0)
	{
		std::wcout<<L"Cannot open the source trajectory file : "<<wstrSrcTrajFilename << L"!\n";
		return -1;
	}
	nRet = loader.Load(trajs);
	if (nRet != 0)
	{
		std::wcout<<L"Cannot load the source trajectory file : "<<wstrSrcTrajFilename << L"!\n";
		return -1;
	}
	loader.Close();
	// Save the portion of the trajectoies
	if (nStartIndex < 0) nStartIndex = 0;
	if (nEndIndex < 0) nEndIndex = trajs.Count();
	if (saver.Initialize(wstrDestTrajFilename) != 0)
	{
		std::wcout<<L"Cannot save the portion of trajectories to file : "<<wstrDestTrajFilename << L"!\n";
		return -1;
	}
	nRet = saver.Save(trajs, nStartIndex, nEndIndex);
	if (nRet != 0)
	{
		std::wcout<<L"Cannot save the dest trajectories to file : "<<wstrDestTrajFilename << L"!\n";
		return -1;
	}
	saver.Close();
	return 0;
}

int TRAODGrid(int argc, wchar_t* argv[])
{
	int nRet;
	if (argc < 3)
	{
		PrintTRAODGridUsage();
		return 0;
	}
	int nComIndex;
	std::wstring wstrTrajFilename = argv[2];
	std::wstring wstrOutlierFilename = argv[3];
	double dOutlyingDist = 0;
	double dOutlyingFrac = 0;
	double dOutlyingPortion = 0;
	double dGridLocSize = 0;
	double dGridDirSize = 0;
	double dSampleSize = 0;
	double dLocWeight = 0;
	double dDirWeight = 0;
	double dOutlierPortionMin = -1.0;
	double dOutlierPortionMax = -1.0;
	double dOutlyingDist2 = -1.0;
	double dOutlyingDistStep = 0;

	for (nComIndex=4; nComIndex<argc;)
	{
		if (_wcsicmp(argv[nComIndex],L"-D") ==  0)
		{
			dOutlyingDist = _wtof(argv[nComIndex+1]);
			nComIndex +=2;
		}
		else if (_wcsicmp(argv[nComIndex],L"-D2") ==  0)
		{
			dOutlyingDist2 = _wtof(argv[nComIndex+1]);
			nComIndex +=2;
		}
		else if (_wcsicmp(argv[nComIndex],L"-Dstep") ==  0)
		{
			dOutlyingDistStep = _wtof(argv[nComIndex+1]);
			nComIndex +=2;
		}
		else if (_wcsicmp(argv[nComIndex],L"-F") ==  0)
		{
			dOutlyingFrac = _wtof(argv[nComIndex+1]);
			nComIndex +=2;
		}
		else if (_wcsicmp(argv[nComIndex],L"-P") ==  0)
		{
			dOutlyingPortion = _wtof(argv[nComIndex+1]);
			nComIndex +=2;
		}
		else if (_wcsicmp(argv[nComIndex],L"-grid") ==  0)
		{
			dGridLocSize = _wtof(argv[nComIndex+1]);
			dGridDirSize = _wtof(argv[nComIndex+2]);
			nComIndex += 3;
		}
		else if (_wcsicmp(argv[nComIndex],L"-sample") ==  0)
		{
			dSampleSize = _wtof(argv[nComIndex+1]);
			nComIndex += 2;
		}
		else if (_wcsicmp(argv[nComIndex], L"-weights") == 0)
		{
			dLocWeight = _wtof(argv[nComIndex+1]);
			dDirWeight = _wtof(argv[nComIndex+2]);
			nComIndex += 3;
		}
		else if (_wcsicmp(argv[nComIndex], L"-ot") == 0)
		{
			dOutlierPortionMin = _wtof(argv[nComIndex+1]);
			dOutlierPortionMax = _wtof(argv[nComIndex+2]);
			nComIndex += 3;
		}
		else
		{
			std::cout<<"Unknown options : ";
			std::wcout<<argv[nComIndex] << L"\n";
			_ASSERT(false);
			return 0;
		}
	}

	// Set the trajectory loader
	CMLTrajectoryLoader loader;
	nRet = loader.Initialize(wstrTrajFilename);
	if (ML_FAILED(nRet))
	{
		std::cout<<"Cannot open the file!";
		std::wcout<<wstrTrajFilename;
		std::cout<<"\n";
		return 0;
	}

	// Load trajectories from txt file
	CMLTrajectoryCollection trajs;
	nRet = loader.Load(trajs);
	if (ML_FAILED(nRet))
	{
		std::cout<<"Load trajectories failed!\n";
		loader.Close();
		return 0;
	}
	loader.Close();
	std::cout<<"Read "<<trajs.Count() << " trajectories.\n";
	long nEndClock;
	long nStartClock;
	int nGridSize = 0;
	CMLTRAODGrid traodGrid;
	std::vector<int> vOutlierTrajIndexes;
	if (dOutlierPortionMin> 0.0 && dOutlyingDist2 > 0.0)
	{
		double d;
		for (d=dOutlyingDist; d<dOutlyingDist2; d+= dOutlyingDistStep)
		{
			// Set the TRAODGrid
			traodGrid.SetPointDimension(2);
			traodGrid.SetOutlyingDist(dOutlyingDist);
			traodGrid.SetOutlyingFrac(dOutlyingFrac);
			traodGrid.SetOutlyingPortion(dOutlyingPortion);
			traodGrid.SetGridLocationSize(dGridLocSize);
			traodGrid.SetGridDirectionSize(dGridDirSize);
			traodGrid.SetTangentPointSampleSize(dSampleSize);
			traodGrid.SetDistWeights(dLocWeight, dDirWeight);

			vOutlierTrajIndexes.clear();
			// Detect the outlier trajectories
			nStartClock = mylablib::CMLTools::GetCurrentClock();
			nRet = traodGrid.Initialize(&trajs);
			ML_ON_FAILED_RETURN(nRet);
			nRet = traodGrid.DetectOutlier(vOutlierTrajIndexes);
			ML_ON_FAILED_RETURN(nRet);
			nEndClock = mylablib::CMLTools::GetCurrentClock();

			std::cout<<"Outliers : "<< vOutlierTrajIndexes.size() << " portion :" << double(vOutlierTrajIndexes.size()) / double(trajs.Count()) << "\n";

			double dPortion = ((double)vOutlierTrajIndexes.size()) / ((double)trajs.Count());
			if (dPortion >= dOutlierPortionMin && dPortion <= dOutlierPortionMax)
				break;
		}
	}
	else
	{

		// Set the TRAODGrid
		traodGrid.SetPointDimension(2);
		traodGrid.SetOutlyingDist(dOutlyingDist);
		traodGrid.SetOutlyingFrac(dOutlyingFrac);
		traodGrid.SetOutlyingPortion(dOutlyingPortion);
		traodGrid.SetGridLocationSize(dGridLocSize);
		traodGrid.SetGridDirectionSize(dGridDirSize);
		traodGrid.SetTangentPointSampleSize(dSampleSize);
		traodGrid.SetDistWeights(dLocWeight, dDirWeight);

		// Detect the outlier trajectories
		nStartClock = mylablib::CMLTools::GetCurrentClock();
		nRet = traodGrid.Initialize(&trajs);
		ML_ON_FAILED_RETURN(nRet);
		nRet = traodGrid.DetectOutlier(vOutlierTrajIndexes);
		ML_ON_FAILED_RETURN(nRet);
		nEndClock = mylablib::CMLTools::GetCurrentClock();
		nGridSize = traodGrid.GetGridSize();
	}

	
	// Print the outliers
	std::cout<<"Outlier Indexes : \n";
	for (int i=0; i<(int)vOutlierTrajIndexes.size(); i++)
	{
		std::cout<<vOutlierTrajIndexes[i]<<" ";
		std::wcout<<trajs.Get(vOutlierTrajIndexes[i]).GetName() << L"\n";
	}
	std::cout<<"Time : "<<nEndClock - nStartClock << "\n";
	std::cout<<"Outliers : "<< vOutlierTrajIndexes.size() << " portion :" << double(vOutlierTrajIndexes.size()) / double(trajs.Count()) << "\n";

	std::wofstream resultFile(wstrOutlierFilename.c_str());
	if (resultFile.fail())
	{
		std::cout<<"Cannot open result file : ";
		std::wcout<<wstrOutlierFilename << L"\n";
		return 0;
	}
	else
	{
		for (int i=0; i<(int)vOutlierTrajIndexes.size(); i++)
		{		
			resultFile<<vOutlierTrajIndexes[i]<<L" ";
		}
		resultFile << L"\n";
		resultFile << L"Time : "<<nEndClock - nStartClock << L"\n";
		resultFile << L"Gridsize : "<<nGridSize << L" Max memory usage: "<<traodGrid.GetMaxMemoryUsage() << L"\n";
		resultFile.close();
	}
	

	return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
	if (argc < 1)
	{
		std::cout<<"No commands!\n";
		return 0;
	}
	if (_wcsicmp(argv[1], L"traodgrid") == 0)
	{
		TRAODGrid(argc, argv);
	}
	else if (_wcsicmp(argv[1], L"trajsplit") == 0)
	{
		TrajsSplit(argc, argv);
	}
	else if (_wcsicmp(argv[1], L"trajsreduce") == 0)
	{
		TrajsReduceDuplicate(argc, argv);
	}
	else
	{
		std::cout<<"No commands!\n";
		return 0;
	}
	return 0;
}

